package com.ideaaedi.zoo.example.springboot.bpmn.flowable.controller;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.IdUtil;
import com.ideaaedi.zoo.diy.artifact.flowable.convert.BpmnEntityConvert;
import com.ideaaedi.zoo.diy.artifact.flowable.facade.BpmnDeployment;
import com.ideaaedi.zoo.diy.artifact.flowable.facade.BpmnProcessDefinition;
import com.ideaaedi.zoo.diy.artifact.flowable.facade.BpmnProcessInstance;
import com.ideaaedi.zoo.diy.artifact.flowable.facade.BpmnTask;
import jakarta.annotation.Resource;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 测试：如果流程已经流转了，此时修改流程变量的话，会触发流程再判断，进而自动调整当前任务节点吗？
 *<pre>
 * 比如 请求>1天，需要负责人审核； 请假<=1天，组长审核， 如果一开始请假时1天，工作流流转到组长审核，
 * 此时，修改流程变量，将请假天数改为2天的话，工作流会自动切换到 负责人审核吗？ 还是说 还是停留在 组长审批？
 *
 * 先给出结论：
 *    如果在流程实例执行中，中途修改流程变量的话（哪怕这个流程变量在之前的流转中起着控制转向的作用），
 *    那么是不会影响到之前已经定下来的节点的。即：并不会触发流程再次重头开始流转
 *</pre>
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@RestController
@RequestMapping("/demo-中途修改流程变量")
public class Demo5中途修改流程变量Controller {
    
    private static final String TENANT = "1,";

    @Resource
    private HistoryService historyService;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;

    @RequestMapping("/test")
    public void test() {
        // step1. 部署资源，并获取 流程定义key
        Deployment deployment = repositoryService.createDeployment()
                .tenantId(TENANT)
                .addBytes("员工请假流程-测试中途修改流程变量.bpmn20.xml",
                        ResourceUtil.readBytes("员工请假流程-测试中途修改流程变量.bpmn20.xml"))
                .name("员工请假流程-测试中途修改流程变量")
                .deploy();
        BpmnDeployment bpmnDeployment = BpmnEntityConvert.convert2BpmnDeployment(deployment);

        List<? extends BpmnProcessDefinition> processDefinitions = Optional.ofNullable(
                repositoryService.createProcessDefinitionQuery()
                        .processDefinitionTenantIdLike(TENANT + "%")
                        .deploymentId(bpmnDeployment.getId())
                        .list()
        ).map(x -> x.stream()
                .map(BpmnEntityConvert::convert2BpmnProcessDefinition)
                .toList()
        ).orElse(Collections.emptyList());

        String processDefinitionKey = processDefinitions.get(0).getKey();

        // step2. 发起流程实例（此时请假1天，那么审核任何应该到组长那里）
        String businessKey = "业务标识_" + IdUtil.fastSimpleUUID();
        Map<String, Object> variables = Map.of("day", 1);
        ProcessInstance processInstance;
        try {
            String userId = null;
            // Flowable发起流程时，会直接从登录态中获取当前用户作为流程发起人
            Authentication.setAuthenticatedUserId(userId);
            processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(processDefinitionKey, businessKey,
                    variables, TENANT);
        } finally {
            Authentication.setAuthenticatedUserId(null);
        }
        BpmnProcessInstance bpmnProcessInstance = BpmnEntityConvert.convert2BpmnProcessInstance(processInstance);
        String processInstanceId = bpmnProcessInstance.getId();

        // step3. 查看此时的任务流转节点
        List<? extends BpmnTask> list = Optional.ofNullable(
                taskService.createTaskQuery()
                        .taskTenantIdLike(TENANT + "%")
                        .processInstanceId(processInstanceId)
                        .list()
        ).map(x -> x.stream()
                .map(BpmnEntityConvert::convert2BpmnTask)
                .toList()
        ).orElse(Collections.emptyList());

        List<String> tipsList = new ArrayList<>();
        // 输出： 当前的任务是：组长
        tipsList.add("当前的任务是：" + list.get(0).getName());

        // step4. 修改流程变量，请假天数为2 （P.S.：正常发起流程的话，请2天假，应该部门负责人进行审批）
        runtimeService.setVariable(list.get(0).getProcessInstanceId(), "day", 2);
        tipsList.add("修改流程变量，请假天数修改为2. （P.S.：正常发起流程的话，请2天假，应该部门负责人进行审批）");


        // step4. 再次查看此时的任务流转节点
        list = Optional.ofNullable(
                taskService.createTaskQuery()
                        .taskTenantIdLike(TENANT + "%")
                        .processInstanceId(processInstanceId)
                        .list()
        ).map(x -> x.stream()
                .map(BpmnEntityConvert::convert2BpmnTask)
                .toList()
        ).orElse(Collections.emptyList());
        // 输出： 当前的任务是：组长
        tipsList.add("此时，查看一下当前的任务节点变了没。 进行查询，当前的任务仍然是：" + list.get(0).getName());


        tipsList.add("可知：如果在流程实例执行中，中途修改流程变量的话（哪怕这个流程变量在之前的流转中起着控制转向的作用），那么是" +
                "不会影响到之前已经定下来的节点的");

        // step5. 删除相关数据
        repositoryService.deleteDeployment(bpmnDeployment.getId(), true);

        for (String tips : tipsList) {
            System.err.println();
            System.err.println(tips);
            try {
                TimeUnit.MILLISECONDS.sleep(20);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}